package znet

import (
	"bytes"
	"encoding/binary"
	"zinx/utils"
	zinterface "zinx/zInterface"
)

// 封包，拆包的具体模块
//这个不需要属性，只需要他的方法
type DataPack struct{}

var d = &DataPack{}

//封包，拆包实例的初始化方法
//个人使用单例模式
func NewDataPack()*DataPack{
	return d
}

//获取包的头的长度的方法--总共8个字节
//dataLen 是uint32，占4个字节 + ID(即数据的类型type)，也是uint32，占4个字节
func(dp *DataPack)GetHeadLen()uint32{
	return 8
}

//封包方法--封包后获得字节流
//dataLen/msgID/data--这是封装后，我们包里数据的格式--封装的时候要注意顺序，不能改
func(dp *DataPack)Pack(msg zinterface.IMessage)([]byte,error){
	// 创建一个存放byte字节的缓冲区
	buf := bytes.NewBuffer([]byte{})

	//1.将dataLen写进buf中--以二进制的形式写进去
	//binary.Write这个函数可以把第三个参数的数据写到第一个参数中去
	//第二个参数是让我们填写这些数据的大小端格式--这个都可以，只要在解析(拆包)时一样即可
	if err := binary.Write(buf,binary.LittleEndian,msg.GetMsgLen());err != nil{
		return nil,err
	}

	//2.将msgID写进buf中
	if err := binary.Write(buf,binary.LittleEndian,msg.GetMsgID());err != nil{
		return nil,err
	}

	//3.将data写进buf中
	if err := binary.Write(buf,binary.LittleEndian,msg.GetMsgData());err != nil{
		return nil,err
	}

	return buf.Bytes(),nil
}

//拆包方法--根据网络传输过来的字节流，解码成固定的格式的数据
//这个要读两次数据
//第一次是将head的信息读出来
//第二次是根据第一次读出来的head信息里的data的数据长度，将data数据读取出来
func(dp *DataPack)UnPack(b []byte)(zinterface.IMessage,error){
	//先创建一个从输入的二进制数据读取数据的io.Reader
	buf := bytes.NewReader(b)
	msg := &Message{}
	// 读取dataLen，写到msg.DataLen中去
	if err := binary.Read(buf,binary.LittleEndian,&msg.DataLen);err != nil{
		return nil,err
	}

	// 读取msgID，写到msg.ID中去
	if err := binary.Read(buf,binary.LittleEndian,&msg.ID);err != nil{
		return nil,err
	}

	// 判断dataLen是否已经超过了我们允许的最大包的长度
	if utils.GlobalObject.MaxPackageSize > 0 && msg.DataLen > utils.GlobalObject.MaxPackageSize{
		return nil,bytes.ErrTooLarge
	}
	return msg,nil
}